<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#函数式编程"  class="anchor" id="user-content-函数式编程">

    </a>函数式编程
    </h1>
    <p>JavaScript 语言从一诞生，就具有函数式编程的烙印。它将函数作为一种独立的数据类型，与其他数据类型处于完全平等的地位。在 JavaScript
        语言中，你可以采用面向对象编程，也可以采用函数式编程。有人甚至说，JavaScript 是有史以来第一种被大规模采用的函数式编程语言。</p>
    <p>ES6 的种种新增功能，使得函数式编程变得更方便、更强大。本章介绍 ES6 如何进行函数式编程。</p>
    <h2><a href="#柯里化"  class="anchor" id="user-content-柯里化">

    </a>柯里化
    </h2>
    <p>柯里化（currying）指的是将一个多参数的函数拆分成一系列函数，每个拆分后的函数都只接受一个参数（unary）。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">add</span> (<span class="pl-smi">a</span>, <span class="pl-smi">b</span>) {
  <span class="pl-k">return</span> a <span class="pl-k">+</span> b;
}

<span class="pl-en">add</span>(<span class="pl-c1">1</span>, <span class="pl-c1">1</span>) <span class="pl-c"><span
                class="pl-c">//</span> 2</span></pre>
    </div>
    <p>上面代码中，函数<code>add</code>接受两个参数<code>a</code>和<code>b</code>。</p>
    <p>柯里化就是将上面的函数拆分成两个函数，每个函数都只接受一个参数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">add</span> (<span class="pl-smi">a</span>) {
  <span class="pl-k">return</span> <span class="pl-k">function</span> (<span class="pl-smi">b</span>) {
    <span class="pl-k">return</span> a <span class="pl-k">+</span> b;
  }
}
<span class="pl-c"><span class="pl-c">//</span> 或者采用箭头函数写法</span>
<span class="pl-k">const</span> <span class="pl-c1">add</span> <span class="pl-k">=</span> <span class="pl-smi">x</span> <span
                class="pl-k">=&gt;</span> <span class="pl-smi">y</span> <span class="pl-k">=&gt;</span> x <span
                class="pl-k">+</span> y;

<span class="pl-k">const</span> <span class="pl-c1">f</span> <span class="pl-k">=</span> <span class="pl-en">add</span>(<span
                class="pl-c1">1</span>);
<span class="pl-en">f</span>(<span class="pl-c1">1</span>) <span class="pl-c"><span
                class="pl-c">//</span> 2</span></pre>
    </div>
    <p>上面代码中，函数<code>add</code>只接受一个参数<code>a</code>，返回一个函数<code>f</code>。函数<code>f</code>也只接受一个参数<code>b</code>。</p>
    <h2><a href="#函数合成"  class="anchor" id="user-content-函数合成">

    </a>函数合成
    </h2>
    <p>函数合成（function composition）指的是，将多个函数合成一个函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">compose</span> <span class="pl-k">=</span> <span class="pl-smi">f</span> <span class="pl-k">=&gt;</span> <span
            class="pl-smi">g</span> <span class="pl-k">=&gt;</span> <span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> <span
            class="pl-en">f</span>(<span class="pl-en">g</span>(x));

<span class="pl-k">const</span> <span class="pl-c1">f</span> <span class="pl-k">=</span> <span
                class="pl-en">compose</span> (<span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> x <span
                class="pl-k">*</span> <span class="pl-c1">4</span>) (<span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> x <span
                class="pl-k">+</span> <span class="pl-c1">3</span>);
<span class="pl-en">f</span>(<span class="pl-c1">2</span>) <span class="pl-c"><span
                class="pl-c">//</span> 20</span></pre>
    </div>
    <p>上面代码中，<code>compose</code>就是一个函数合成器，用于将两个函数合成一个函数。</p>
    <p>可以发现，柯里化与函数合成有着密切的联系。前者用于将一个函数拆成多个函数，后者用于将多个函数合并成一个函数。</p>
    <h2><a href="#参数倒置"  class="anchor" id="user-content-参数倒置">

    </a>参数倒置
    </h2>
    <p>参数倒置（flip）指的是改变函数前两个参数的顺序。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> <span
            class="pl-en">divide</span> <span class="pl-k">=</span> (<span class="pl-smi">a</span>, <span
            class="pl-smi">b</span>) <span class="pl-k">=&gt;</span> a <span class="pl-k">/</span> b;
<span class="pl-k">var</span> flip <span class="pl-k">=</span> <span class="pl-smi">f</span>.<span
                class="pl-en">flip</span>(divide);

<span class="pl-en">flip</span>(<span class="pl-c1">10</span>, <span class="pl-c1">5</span>) <span class="pl-c"><span
                class="pl-c">//</span> 0.5</span>
<span class="pl-en">flip</span>(<span class="pl-c1">1</span>, <span class="pl-c1">10</span>) <span class="pl-c"><span
                class="pl-c">//</span> 10</span>

<span class="pl-k">var</span> <span class="pl-en">three</span> <span class="pl-k">=</span> (<span
                class="pl-smi">a</span>, <span class="pl-smi">b</span>, <span class="pl-smi">c</span>) <span
                class="pl-k">=&gt;</span> [a, b, c];
<span class="pl-k">var</span> flip <span class="pl-k">=</span> <span class="pl-smi">f</span>.<span
                class="pl-en">flip</span>(three);
<span class="pl-en">flip</span>(<span class="pl-c1">1</span>, <span class="pl-c1">2</span>, <span class="pl-c1">3</span>); <span
                class="pl-c"><span class="pl-c">//</span> =&gt; [2, 1, 3]</span></pre>
    </div>
    <p>上面代码中，如果按照正常的参数顺序，10 除以 5 等于 2。但是，参数倒置以后得到的新函数，结果就是 5 除以 10，结果得到 0.5。如果原函数有 3 个参数，则只颠倒前两个参数的位置。</p>
    <p>参数倒置的代码非常简单。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> f <span class="pl-k">=</span> {};
<span class="pl-smi">f</span>.<span class="pl-smi">flip</span> <span class="pl-k">=</span>
  <span class="pl-smi">fn</span> <span class="pl-k">=&gt;</span>
    (<span class="pl-smi">a</span>, <span class="pl-smi">b</span>, <span class="pl-k">...</span><span
                class="pl-v">args</span>) <span class="pl-k">=&gt;</span> <span class="pl-en">fn</span>(b, a, <span
                class="pl-k">...</span><span class="pl-smi">args</span>.<span class="pl-c1">reverse</span>());</pre>
    </div>
    <h2><a href="#执行边界"  class="anchor" id="user-content-执行边界">

    </a>执行边界
    </h2>
    <p>执行边界（until）指的是函数执行到满足条件为止。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> <span
            class="pl-en">condition</span> <span class="pl-k">=</span> <span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> x <span
            class="pl-k">&gt;</span> <span class="pl-c1">100</span>;
<span class="pl-k">let</span> <span class="pl-en">inc</span> <span class="pl-k">=</span> <span
                class="pl-smi">x</span> <span class="pl-k">=&gt;</span> x <span class="pl-k">+</span> <span
                class="pl-c1">1</span>;
<span class="pl-k">let</span> until <span class="pl-k">=</span> <span class="pl-smi">f</span>.<span
                class="pl-en">until</span>(condition, inc);

<span class="pl-en">until</span>(<span class="pl-c1">0</span>) <span class="pl-c"><span
                class="pl-c">//</span> 101</span>

<span class="pl-en">condition</span> <span class="pl-k">=</span> <span class="pl-smi">x</span> <span
                class="pl-k">=&gt;</span> x <span class="pl-k">===</span> <span class="pl-c1">5</span>;
until <span class="pl-k">=</span> <span class="pl-smi">f</span>.<span class="pl-en">until</span>(condition, inc);

<span class="pl-en">until</span>(<span class="pl-c1">3</span>) <span class="pl-c"><span class="pl-c">//</span> 5</span></pre>
    </div>
    <p>上面代码中，第一段的条件是执行到<code>x</code>大于 100 为止，所以<code>x</code>初值为 0 时，会一直执行到 101。第二段的条件是执行到等于 5 为止，所以<code>x</code>最后的值是
        5。</p>
    <p>执行边界的实现如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> f <span class="pl-k">=</span> {};
<span class="pl-smi">f</span>.<span class="pl-en">until</span> <span class="pl-k">=</span> (<span class="pl-smi">condition</span>, <span
                class="pl-smi">f</span>) <span class="pl-k">=&gt;</span>
  (<span class="pl-k">...</span><span class="pl-v">args</span>) <span class="pl-k">=&gt;</span> {
    <span class="pl-k">var</span> r <span class="pl-k">=</span> <span class="pl-smi">f</span>.<span
                class="pl-c1">apply</span>(<span class="pl-c1">null</span>, args);
    <span class="pl-k">return</span> <span class="pl-en">condition</span>(r) <span class="pl-k">?</span> r <span
                class="pl-k">:</span> <span class="pl-smi">f</span>.<span class="pl-en">until</span>(condition, f)(r);
  };</pre>
    </div>
    <p>上面代码的关键就是，如果满足条件就返回结果，否则不断递归执行。</p>
    <h2><a href="#队列操作"  class="anchor" id="user-content-队列操作">

    </a>队列操作
    </h2>
    <p>队列（list）操作包括以下几种。</p>
    <ul>
        <li><code>head</code>： 取出队列的第一个非空成员。</li>
        <li><code>last</code>： 取出有限队列的最后一个非空成员。</li>
        <li><code>tail</code>： 取出除了“队列头”以外的其他非空成员。</li>
        <li><code>init</code>： 取出除了“队列尾”以外的其他非空成员。</li>
    </ul>
    <p>下面是例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">f</span>.<span class="pl-en">head</span>(<span
            class="pl-c1">5</span>, <span class="pl-c1">27</span>, <span class="pl-c1">3</span>, <span
            class="pl-c1">1</span>) <span class="pl-c"><span class="pl-c">//</span> 5</span>
<span class="pl-smi">f</span>.<span class="pl-en">last</span>(<span class="pl-c1">5</span>, <span
                class="pl-c1">27</span>, <span class="pl-c1">3</span>, <span class="pl-c1">1</span>) <span class="pl-c"><span
                class="pl-c">//</span> 1</span>
<span class="pl-smi">f</span>.<span class="pl-en">tail</span>(<span class="pl-c1">5</span>, <span
                class="pl-c1">27</span>, <span class="pl-c1">3</span>, <span class="pl-c1">1</span>) <span class="pl-c"><span
                class="pl-c">//</span> [27, 3, 1]</span>
<span class="pl-smi">f</span>.<span class="pl-en">init</span>(<span class="pl-c1">5</span>, <span
                class="pl-c1">27</span>, <span class="pl-c1">3</span>, <span class="pl-c1">1</span>) <span class="pl-c"><span
                class="pl-c">//</span> [5, 27, 3]</span></pre>
    </div>
    <p>这些方法的实现如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> f <span class="pl-k">=</span> {};
<span class="pl-smi">f</span>.<span class="pl-en">head</span> <span class="pl-k">=</span> (<span class="pl-k">...</span><span
                class="pl-v">xs</span>) <span class="pl-k">=&gt;</span> xs[<span class="pl-c1">0</span>];
<span class="pl-smi">f</span>.<span class="pl-en">last</span> <span class="pl-k">=</span> (<span class="pl-k">...</span><span
                class="pl-v">xs</span>) <span class="pl-k">=&gt;</span> <span class="pl-smi">xs</span>.<span
                class="pl-c1">slice</span>(<span class="pl-k">-</span><span class="pl-c1">1</span>);
<span class="pl-smi">f</span>.<span class="pl-en">tail</span> <span class="pl-k">=</span> (<span class="pl-k">...</span><span
                class="pl-v">xs</span>) <span class="pl-k">=&gt;</span> <span class="pl-c1">Array</span>.<span
                class="pl-c1">prototype</span>.<span class="pl-smi">slice</span>.<span
                class="pl-c1">call</span>(xs, <span class="pl-c1">1</span>);
<span class="pl-smi">f</span>.<span class="pl-en">init</span> <span class="pl-k">=</span> (<span class="pl-k">...</span><span
                class="pl-v">xs</span>) <span class="pl-k">=&gt;</span> <span class="pl-smi">xs</span>.<span
                class="pl-c1">slice</span>(<span class="pl-c1">0</span>, <span class="pl-k">-</span><span class="pl-c1">1</span>);</pre>
    </div>
    <h2><a href="#合并操作"  class="anchor" id="user-content-合并操作">

    </a>合并操作
    </h2>
    <p>合并操作分为<code>concat</code>和<code>concatMap</code>两种。前者就是将多个数组合成一个，后者则是先处理一下参数，然后再将处理结果合成一个数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">f</span>.<span
            class="pl-c1">concat</span>([<span class="pl-c1">5</span>], [<span class="pl-c1">27</span>], [<span
            class="pl-c1">3</span>]) <span class="pl-c"><span class="pl-c">//</span> [5, 27, 3]</span>
<span class="pl-smi">f</span>.<span class="pl-en">concatMap</span>(<span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> <span
                class="pl-s"><span class="pl-pds">'</span>hi <span class="pl-pds">'</span></span> <span
                class="pl-k">+</span> x, <span class="pl-c1">1</span>, [[<span class="pl-c1">2</span>]], <span
                class="pl-c1">3</span>) <span class="pl-c"><span class="pl-c">//</span> ['hi 1', 'hi 2', 'hi 3']</span></pre>
    </div>
    <p>这两种方法的实现代码如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> f <span class="pl-k">=</span> {};
<span class="pl-smi">f</span>.<span class="pl-smi">concat</span> <span class="pl-k">=</span>
  (<span class="pl-k">...</span><span class="pl-v">xs</span>) <span class="pl-k">=&gt;</span> <span
                class="pl-smi">xs</span>.<span class="pl-en">reduce</span>((<span class="pl-smi">a</span>, <span
                class="pl-smi">b</span>) <span class="pl-k">=&gt;</span> <span class="pl-smi">a</span>.<span
                class="pl-c1">concat</span>(b));
<span class="pl-smi">f</span>.<span class="pl-smi">concatMap</span> <span class="pl-k">=</span>
  (<span class="pl-smi">f</span>, <span class="pl-k">...</span><span class="pl-v">xs</span>) <span
                class="pl-k">=&gt;</span> <span class="pl-smi">f</span>.<span class="pl-c1">concat</span>(<span
                class="pl-smi">xs</span>.<span class="pl-en">map</span>(f));</pre>
    </div>
    <h2><a href="#配对操作"  class="anchor" id="user-content-配对操作">

    </a>配对操作
    </h2>
    <p>配对操作分为<code>zip</code>和<code>zipWith</code>两种方法。<code>zip</code>操作将两个队列的成员，一一配对，合成一个新的队列。如果两个队列不等长，较长的那个队列多出来的成员，会被忽略。<code>zipWith</code>操作的第一个参数是一个函数，然后会将后面的队列成员一一配对，输入该函数，返回值就组成一个新的队列。
    </p>
    <p>下面是例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> a <span class="pl-k">=</span> [<span
            class="pl-c1">0</span>, <span class="pl-c1">1</span>, <span class="pl-c1">2</span>];
<span class="pl-k">let</span> b <span class="pl-k">=</span> [<span class="pl-c1">3</span>, <span class="pl-c1">4</span>, <span
                class="pl-c1">5</span>];
<span class="pl-k">let</span> c <span class="pl-k">=</span> [<span class="pl-c1">6</span>, <span class="pl-c1">7</span>, <span
                class="pl-c1">8</span>];

<span class="pl-smi">f</span>.<span class="pl-en">zip</span>(a, b) <span class="pl-c"><span class="pl-c">//</span> [[0, 3], [1, 4], [2, 5]]</span>
<span class="pl-smi">f</span>.<span class="pl-en">zipWith</span>((<span class="pl-smi">a</span>, <span
                class="pl-smi">b</span>) <span class="pl-k">=&gt;</span> a <span class="pl-k">+</span> b, a, b, c) <span
                class="pl-c"><span class="pl-c">//</span> [9, 12, 15]</span></pre>
    </div>
    <p>上面代码中，<code>zipWith</code>方法的第一个参数是一个求和函数，它将后面三个队列的成员，一一配对进行相加。</p>
    <p>这两个方法的实现如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> f <span class="pl-k">=</span> {};

<span class="pl-smi">f</span>.<span class="pl-en">zip</span> <span class="pl-k">=</span> (<span
                class="pl-k">...</span><span class="pl-v">xs</span>) <span class="pl-k">=&gt;</span> {
  <span class="pl-k">let</span> r <span class="pl-k">=</span> [];
  <span class="pl-k">let</span> nple <span class="pl-k">=</span> [];
  <span class="pl-k">let</span> length <span class="pl-k">=</span> <span class="pl-c1">Math</span>.<span class="pl-smi">min</span>.<span
                class="pl-c1">apply</span>(<span class="pl-c1">null</span>, <span class="pl-smi">xs</span>.<span
                class="pl-en">map</span>(<span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> <span
                class="pl-smi">x</span>.<span class="pl-c1">length</span>));

  <span class="pl-k">for</span> (<span class="pl-k">var</span> i <span class="pl-k">=</span> <span
                class="pl-c1">0</span>; i <span class="pl-k">&lt;</span> length; i<span class="pl-k">++</span>) {
    <span class="pl-smi">xs</span>.<span class="pl-c1">forEach</span>(
      <span class="pl-smi">x</span> <span class="pl-k">=&gt;</span> <span class="pl-smi">nple</span>.<span
                class="pl-c1">push</span>(x[i])
    );

    <span class="pl-smi">r</span>.<span class="pl-c1">push</span>(nple);
    nple <span class="pl-k">=</span> [];
  }

  <span class="pl-k">return</span> r;
};

<span class="pl-smi">f</span>.<span class="pl-en">zipWith</span> <span class="pl-k">=</span> (<span
                class="pl-smi">op</span>, <span class="pl-k">...</span><span class="pl-v">xs</span>) <span class="pl-k">=&gt;</span>
  <span class="pl-smi">f</span>.<span class="pl-smi">zip</span>.<span class="pl-c1">apply</span>(<span class="pl-c1">null</span>, xs).<span
                class="pl-en">map</span>(
    (<span class="pl-smi">x</span>) <span class="pl-k">=&gt;</span> <span class="pl-smi">x</span>.<span class="pl-en">reduce</span>(op)
  );</pre>
    </div>
    <h2><a href="#参考链接"  class="anchor" id="user-content-参考链接">

    </a>参考链接
    </h2>
    <ul>
        <li>Mateo Gianolio, <a href="http://casualjavascript.com/?1" rel="nofollow">Haskell in ES6: Part 1</a></li>
    </ul>
</div>
</body>
</html>
